#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# conversation_buffer.py
#
#  Copyright (c) 2007 INdT (Instituto Nokia de Technologia)
#                2007 Kenneth Christiansen
#
#  Author: Kenneth Rohde Christiansen <kenneth.christiansen@gmail.com>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import pygtk
pygtk.require("2.0")
import gtk
import gobject
import pango
import tapioca
import icon_theme
import os
import sys
import re
from html_buffer import HtmlBuffer

class ConversationBuffer(object):

    def __init__(self, emoticons):
        self.emoticons = emoticons

        self._make_regexps()

        self.html_buffer = HtmlBuffer()
        self.text_buffer = gtk.TextBuffer()

        self.text_buffer.create_tag("bold", weight=pango.WEIGHT_BOLD)
        self.text_buffer.create_tag("italic", style=pango.STYLE_ITALIC)
        self.text_buffer.create_tag("underline", underline=pango.UNDERLINE_SINGLE)
        self.text_buffer.create_tag("gray", foreground='gray')
        self.text_buffer.create_tag("invisible", invisible=True)
        self.text_buffer.create_tag("marked", background='yellow')

        tag = self.text_buffer.create_tag('uri')
        tag.set_property('foreground', "blue")
        tag.set_property('underline', pango.UNDERLINE_SINGLE)
        tag.connect('event', self.on_uri_clicked, 'uri')


    def get_html_buffer(self):
        return self.html_buffer


    def get_text_buffer(self):
        return self.text_buffer


    def _make_regexps(self):
        """
        Generated the regular expressions uses to match special text
        to be inserted into the conversation text buffer of the
        chat window.
        """

        # Detect links that we can open
        links = r'\bhttp://\S*[^\s\W]|' r'\bhttps://\S*[^\s\W]|' \
            r'\bnews://\S*[^\s\W]|' r'\bftp://\S*[^\s\W]|' \
            r'\bed2k://\S*[^\s\W]|' r'\bwww\.\S*[^\s\W]|' \
            r'\bftp\.\S*[^\s\W]|'

        # Detect valid email addresses
        mail = r'\bmailto:\S*[^\s\W]|' r'\b\S+@\S+\.\S*[^\s\W]|'

        # Detect *b* *bold* *bold bold* test *bold*
        # Do not detect * bold* *bold * * bold * test*bold*
        formatting = r'(?<!\S)\*[^\s*]([^*]*[^\s*])?\*(?!\S)|' \
                     r'(?<!\S)/[^\s/]([^/]*[^\s/])?/(?!\S)|' \
                     r'(?<!\S)_[^\s_]([^_]*[^\s_])?_(?!\S)'

        basic_pattern = links + mail + formatting

        self.basic_pattern_re = re.compile(basic_pattern)

        emotes_pattern = ''

        for emote in self.emoticons.get_emoticon_pixbufs().keys():
            emote_escaped = re.escape(emote)
            emotes_pattern += emote_escaped + '|'

        emote_and_basic_pattern = emotes_pattern + basic_pattern
        self.emote_and_basic_re = re.compile(emote_and_basic_pattern)

        # at least one character in 3 parts (before @, after @, after .)
        self.sth_at_sth_dot_sth_re = re.compile(r'\S+@\S+\.\S*[^\s)?]')


    def set_text(self, text):
        self.text_buffer.set_text("")
        self.html_buffer.set_text("")

    def get_char_count(self):
        return self.text_buffer.get_char_count()


    def get_end_iter(self):
        return self.text_buffer.get_end_iter()


    def detect_and_insert_special_text(self, text, tags=[]):
        """
        Function that detects special text and insert it. 
        Eventually the function is called again on the 
        same text to find additional special text. For this
        reason already found tags to be applied when printing
        are passed along. (Based on Gajim function).

        When used the first time, please call with an empty
        tags=[] list, as python seem to reuse the one from
        last execution.
        """

        start = 0
        end = 0
        index = 0

        iter = self.emote_and_basic_re.finditer(text)

        for match in iter:
            start, end = match.span()
            special_text = text[start:end]

            if start != 0:
                text_before_special_text = text[index:start]
                end_iter = self.text_buffer.get_end_iter()

                self.text_buffer.insert(end_iter, text_before_special_text)
                self.html_buffer.insert(text_before_special_text)

            index = end

            self.insert_special_text(special_text, tags)

        if index != len(text):
            end_iter = self.text_buffer.get_end_iter()
            self.insert_with_tag_list(text[index:], end_iter, tags)

        return index


    def insert(self, iter, message):
        self.text_buffer.insert(iter, message)
        self.html_buffer.insert(message)


    def insert_with_tags_by_name(self, iter, message, *tags):
        self.text_buffer.insert_with_tags_by_name(iter, message, *tags)

        for tag in tags:
            message = self._apply_tag_as_html(message, tag)

        self.html_buffer.insert(message)


    def insert_special_text(self, special_text, tags=[]):
        use_other_tags = False

        possible_emot_ascii_caps = special_text

        emoticon_pixbufs = self.emoticons.get_emoticon_pixbufs()

        if possible_emot_ascii_caps in emoticon_pixbufs.keys():
            emot_ascii = possible_emot_ascii_caps
            end_iter = self.text_buffer.get_end_iter()
            self.text_buffer.insert_pixbuf(end_iter, emoticon_pixbufs[emot_ascii])
            # extra space added because the pixbuf occupies one space
            self.html_buffer.insert("<img alt=\"" + emot_ascii + "\"/> ")

            return

        elif special_text.startswith('mailto:'):
            tags.append('mail')
            use_other_tags = False

        elif self.sth_at_sth_dot_sth_re.match(special_text):
            tags.append('mail')
            use_other_tags = False

        elif special_text.startswith('*'): # it's a bold text
            tags.append('bold')
            self.detect_and_insert_special_text(special_text[1:-1], tags)

            return

        elif special_text.startswith('/'): # it's an italic text
            tags.append('italic')
            self.detect_and_insert_special_text(special_text[1:-1], tags)

            return

        elif special_text.startswith('_'): # it's an underlined text
            tags.append('underline')
            self.detect_and_insert_special_text(special_text[1:-1], tags)

            return

        else:
            tags.append('uri')
            use_other_tags = False

        if len(tags) > 0:
            end_iter = self.text_buffer.get_end_iter()
            all_tags = tags[:]

            if use_other_tags:
                all_tags += other_tags

            self.insert_with_tag_list(special_text, end_iter, all_tags)


    def insert_with_tag_list(self, text, iter, tag_list):
        begin_mark = self.text_buffer.create_mark('begin_tag', iter, True)
        self.text_buffer.insert(iter, text)
        begin_tagged = self.text_buffer.get_iter_at_mark(begin_mark)

        for tag in tag_list:
            self.text_buffer.apply_tag_by_name(tag, begin_tagged, iter)
            text = self._apply_tag_as_html(text, tag)

        self.html_buffer.insert(text)

        self.text_buffer.delete_mark(begin_mark)


    def _apply_tag_as_html(self, text, tag):
        if tag == "bold":
            text = "<b>" + text + "</b>"
        if tag == "italic":
            text = "<i>" + text + "</i>"
        if tag == "underline":
            text = "<u>" + text + "</u>"

        return text


    def on_uri_clicked(self, tag, widget, event, end_iter, kind):
        """
        Opens an URI when clicked on, using gnome-open.
        """

        if event.type == gtk.gdk.BUTTON_PRESS:
            begin_iter = end_iter.copy()

            while not begin_iter.begins_tag(tag):
                begin_iter.backward_char()

            end_iter = end_iter.copy()

            while not end_iter.ends_tag(tag):
                end_iter.forward_char()

            #uri = self.conversation_buffer.get_text(begin_iter, end_iter)
            uri = self.text_buffer.get_text(begin_iter, end_iter)
            uri = uri.decode('utf-8')

            if kind == 'uri' and not (uri.startswith('http://') or uri.startswith('https://')):
                uri = 'http://' + uri
            elif kind == 'mail' and not uri.startswith('mailto:'):
                uri = 'mailto:' + uri

            uri = uri.replace('"', '\\"') 
            command = 'gnome-open "' + uri + '" &'

            try:
                os.system(command)
            except:
                pass

